package com.giscloud.commons.utils;

import com.giscloud.commons.exception.BusinessException;
import lombok.extern.log4j.Log4j2;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author giscloud
 * @data 2018/7/10 11:55
 */
@Log4j2
public class HttpClientUtil {

    private final static int SOCKET_TIME_OUT = 30000;
    private final static int CONNECT_TIME_OUT = 30000;
    private final static int READ_TIME_OUT = 30000;

    private static CloseableHttpClient createSSLInsecureClient() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf =
                    new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
                        @Override
                        public boolean verify(String arg0, SSLSession arg1) {
                            return true;
                        }

                        @Override
                        public void verify(String host, SSLSocket ssl) throws IOException {
                        }

                        @Override
                        public void verify(String host, X509Certificate cert) throws SSLException {
                        }

                        @Override
                        public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
                        }
                    });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (GeneralSecurityException e) {
            log.error("Create ssl socket factory error!{}", e);
            return null;
        }
    }

    public static String getConnectUrlData(String url,Map<String,String> headerMap) {
        return getConnectUrlData( url, headerMap,SOCKET_TIME_OUT,CONNECT_TIME_OUT,READ_TIME_OUT);
    }

        /**
         * 拼接url   get请求
         * @param url
         * @param headerMap
         * @return
         */

    public static String getConnectUrlData(String url,Map<String,String> headerMap,int socketTimeout,int connectionTimeout,int readTimeout){
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if (httpClient == null) {
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }
        // 创建HttpGet请求
        int status = -1;
        String result = null;
        HttpGet httpGet = null;
        CloseableHttpResponse response = null;
        try {

            if(!headerMap.isEmpty()){
                url=url+"?";
                for(String key : headerMap.keySet()){
                    url = url+key+"="+headerMap.get(key)+"&";
                }
            }
            // 设置请求头部
            httpGet = new HttpGet(url);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(socketTimeout)
                    .setConnectTimeout(connectionTimeout)
                    .setConnectionRequestTimeout(readTimeout)
                    .build();
            httpGet.setConfig(requestConfig);
            // 执行get请求.
            response = httpClient.execute(httpGet);
            if (null != response && response.getStatusLine() != null) {
                status = response.getStatusLine().getStatusCode();
                if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    // 获取响应实体
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
        } catch (Exception e) {
            log.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
            throw new BusinessException("发送get请求出现异常");
        } finally {
            closeResource(response, httpGet, httpClient);
        }

        return result;
    }
    /**
     * 发送GET类型的HTTP请求，
     *
     * @param url 请求的URL地址
     * @return 返回请求连接相应的json字符串
     */
    public static String getData(String url) {
        return getData(url, null);
    }

    public static String getData(String url, Map<String, String> headerMap){
        return getData( url, headerMap,SOCKET_TIME_OUT,CONNECT_TIME_OUT,READ_TIME_OUT);
    }

    /**
     * 发送GET类型的HTTP请求，
     *
     * @param url       请求的URL地址
     * @param headerMap 设置Header
     * @return 返回请求连接相应的json字符串
     */
    public static String getData(String url, Map<String, String> headerMap,int socketTimeout,int connectionTimeout,int readTimeout) {
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if (httpClient == null) {
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }
        // 创建HttpGet请求
        int status = -1;
        String result = null;
        HttpGet httpGet = null;
        CloseableHttpResponse response = null;
        try {
            // 设置请求头部
            httpGet = new HttpGet(url);
            Header[] headers = toHeaders(headerMap);
            if (null != headers && headers.length > 0) {
                httpGet.setHeaders(headers);
            }
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(socketTimeout)
                    .setConnectTimeout(connectionTimeout)
                    .setConnectionRequestTimeout(readTimeout)
                    .build();
            httpGet.setConfig(requestConfig);
            // 执行get请求.
            response = httpClient.execute(httpGet);
            if (null != response && response.getStatusLine() != null) {
                status = response.getStatusLine().getStatusCode();
                if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    // 获取响应实体
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
        } catch (Exception e) {
            log.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
            throw new BusinessException("发送get请求出现异常");
        } finally {
            closeResource(response, httpGet, httpClient);
        }

        return result;
    }


    public static byte[] getByte(String url){
        return getByte( url,SOCKET_TIME_OUT,CONNECT_TIME_OUT, READ_TIME_OUT);
    }
    /**
     * 通过Get方法请求url获取字节数据
     *
     * @param url 请求地址
     * @return 字节数据，失败为null
     */
    public static byte[] getByte(String url,int socketTimeout,int connectionTimeout,int readTimeout) {
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if (httpClient == null) {
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }
        // 创建HttpGet请求
        int status = -1;
        byte data[] = null;
        HttpGet httpGet = null;
        CloseableHttpResponse response = null;
        try {

            httpGet = new HttpGet(url);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(socketTimeout)
                    .setConnectTimeout(connectionTimeout)
                    .setConnectionRequestTimeout(readTimeout)
                    .build();
            httpGet.setConfig(requestConfig);
            response = httpClient.execute(httpGet);
            if (null != response && response.getStatusLine() != null) {
                status = response.getStatusLine().getStatusCode();
                if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    // 获取响应实体
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        data = EntityUtils.toByteArray(entity);
                    }
                }
            }
        } catch (IOException e) {
            log.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
            throw new BusinessException("发送get请求出现异常");
        } finally {
            closeResource(response, httpGet, httpClient);
        }

        return data;
    }


    public static String postFormMultipart(String url,Map<String,String> params, List<File> files,Map<String,String> headers,String encode) {
      return  postFormMultipart( url,params, files,headers, encode, SOCKET_TIME_OUT,CONNECT_TIME_OUT, READ_TIME_OUT);
    }
        /**
         * 发送 http post 请求，支持文件上传
         */
    public static String postFormMultipart(String url,Map<String,String> params, List<File> files,Map<String,String> headers,String encode,int socketTimeout,int connectionTimeout,int readTimeout){
        if(encode == null){
            encode = "utf-8";
        }
        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
        HttpPost httpost = new HttpPost(url);
        //设置header
        if (headers != null && headers.size() > 0) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpost.setHeader(entry.getKey(),entry.getValue());
            }
        }
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectionTimeout)
                .setConnectionRequestTimeout(readTimeout)
                .build();
        httpost.setConfig(requestConfig);
        MultipartEntityBuilder mEntityBuilder = MultipartEntityBuilder.create();
        mEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
        mEntityBuilder.setCharset(Charset.forName(encode));

        // 普通参数
        ContentType contentType = ContentType.create("text/plain",Charset.forName(encode));//解决中文乱码
        if (params != null && params.size() > 0) {
            Set<String> keySet = params.keySet();
            for (String key : keySet) {
                mEntityBuilder.addTextBody(key, params.get(key),contentType);
            }
        }
        //二进制参数
        if (files != null && files.size() > 0) {
            for (File file : files) {
                mEntityBuilder.addBinaryBody("file", file);
            }
        }
        httpost.setEntity(mEntityBuilder.build());
        String content = null;
        CloseableHttpResponse httpResponse = null;
        try {
            httpResponse = closeableHttpClient.execute(httpost);
            HttpEntity entity = httpResponse.getEntity();
            content = EntityUtils.toString(entity, encode);
            return content;
        } catch (Exception e) {
            log.error("postFormMultipart failed, HTTP status code: {}, url: {}", url, e);
            throw new BusinessException("发送post请求出现异常");
        }finally{
            closeResource(httpResponse, httpost, closeableHttpClient);
        }
    }

    /**
     * 发送 post请求访问本地应用并根据传递参数不同返回不同结果
     * 不带头信息
     * @param url      post请求的链接
     * @param paramMap 请求的参数Map
     */
    public static String postData(String url, Map<String, String> paramMap) {
        return postData(url, null, paramMap);
    }
    /**
     * 不带头信息
     * @param url
     * @param paramMap
     * @param connectionTimeout
     * @param readTimeout
     * @return
     */
    public static String postData(String url, Map<String, String> paramMap ,int socketTimeout,int connectionTimeout,int readTimeout) {
        return postData(url, null, paramMap,socketTimeout,connectionTimeout,readTimeout);
    }
    /**
     * 带头信息
     * @param url
     * @param headerMap
     * @param paramMap
     * @return
     */
    public static String postData(String url, Map<String, String> headerMap, Map<String, String> paramMap) {
        return postData(url, headerMap, paramMap, SOCKET_TIME_OUT,CONNECT_TIME_OUT, READ_TIME_OUT);
    }

    public static String postData(String url, Map<String, String> headerMap, Map<String, String> paramMap,int socketTimeout,int connectionTimeout,int readTimeout) {
        // 创建默认的httpClient实例.
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if (httpClient == null) {
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectionTimeout)
                .setConnectionRequestTimeout(readTimeout)
                .build();
        // 创建httpPost
        int status = -1;
        String result = null;
        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        try {

            // 设置请求头部
            httpPost = new HttpPost(url);
            Header[] headers = toHeaders(headerMap);
            if (null != headers && headers.length > 0) {
                httpPost.setHeaders(headers);
            }
            httpPost.setConfig(requestConfig);
            // 设置请求参数
            List<NameValuePair> valuePairs = toNameValuePairList(paramMap);
            if (null != valuePairs && !valuePairs.isEmpty()) {
                httpPost.setEntity(new UrlEncodedFormEntity(valuePairs, "UTF-8"));
            }
            response = httpClient.execute(httpPost);
            if (null != response && response.getStatusLine() != null) {
                status = response.getStatusLine().getStatusCode();
                if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
        } catch (Exception e) {
            log.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
            throw new BusinessException("发送post请求出现异常");
        } finally {
            closeResource(response, httpPost, httpClient);
        }

        return result;
    }

    /**
     * 发送post json数据
     * @param url 请求地址
     * @param headerMap 头信息
     * @param paramMap 参数信息
     * @return
     */
    public static String postDataByJson(String url, Map<String, String> headerMap, Map<String, Object> paramMap) {
        return postDataByJson(url,headerMap,paramMap,SOCKET_TIME_OUT,CONNECT_TIME_OUT,READ_TIME_OUT);
    }

    public static String postDataByJson(String url, Map<String, String> headerMap, Map<String, Object> paramMap,int socketTimeout,int connectTimeout,int readTimeout) {
        // 创建默认的httpClient实例.
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if (httpClient == null) {
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }
        // 创建httpPost
        int status = -1;
        String result = null;
        HttpPost httpPost = null;
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(readTimeout)
                .build();
        CloseableHttpResponse response = null;
        try {
            // 设置请求头部
            httpPost = new HttpPost(url);
            Header[] headers = toHeaders(headerMap);
            if (null != headers && headers.length > 0) {
                httpPost.setHeaders(headers);
            }
            httpPost.addHeader("Content-type", "application/json; charset=utf-8");
            httpPost.setConfig(requestConfig);
            // 设置请求参数
            String params = FastJsonUtils.toJSONString(paramMap);
            if (StringUtils.isNotBlank(params)) {
                httpPost.setEntity(new StringEntity(params, Charset.forName("UTF-8")));
            }
            response = httpClient.execute(httpPost);
            if (null != response && response.getStatusLine() != null) {
                status = response.getStatusLine().getStatusCode();
                if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
        } catch (Exception e) {
            log.error("Request failed, HTTP status code: {}, url: {}", status, url, e);
            throw new BusinessException("发送post请求出现异常");
        } finally {
            closeResource(response, httpPost, httpClient);
        }

        return result;
    }



    /**
     * 发送post请求，参数为字符串
     *
     * @param url  请求地址
     * @param post 请求数据
     * @return 返回的内容
     */
    public static String postData(String url, String post) {
        return postData(url,post,SOCKET_TIME_OUT,CONNECT_TIME_OUT,READ_TIME_OUT);
    }
    public static String postData(String url, String post,int socketTimeout,int connectTimeout,int readTimeout) {
        // 创建默认的httpClient实例.
        CloseableHttpClient httpClient = null;
        if (url.startsWith("https://")) {
            httpClient = createSSLInsecureClient();
            if (httpClient == null) {
                return null;
            }
        } else {
            httpClient = HttpClients.createDefault();
        }
        // 创建httpPost
        int status = -1;
        String result = null;
        HttpPost httpPost = null;
        CloseableHttpResponse response = null;
        try {
            // 设置请求头部
            httpPost = new HttpPost(url);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setSocketTimeout(socketTimeout)
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(readTimeout)
                    .build();
            httpPost.setConfig(requestConfig);
            // 设置请求参数
            StringEntity stringEntity = new StringEntity(post, ContentType.APPLICATION_JSON);
            httpPost.setEntity(stringEntity);
            // 执行post请求
            response = httpClient.execute(httpPost);
            if (null != response && response.getStatusLine() != null) {
                status = response.getStatusLine().getStatusCode();
                if (status == HttpStatus.SC_OK || status == HttpStatus.SC_CREATED) {
                    HttpEntity entity = response.getEntity();
                    if (null != entity) {
                        result = EntityUtils.toString(entity, "UTF-8");
                    }
                }
            }
        } catch (Exception e) {
            log.error("Request failed, HTTP status code: {}, url: {},error:{}", status, url, e);
            throw new BusinessException("发送post请求出现异常");
        } finally {
            closeResource(response, httpPost, httpClient);
        }
        return result;

    }

    /**
     * 发送 http put 请求，参数以原生字符串进行提交
     * @param url
     * @param encode
     * @return
     */
    public static String putData(String url,String stringJson,Map<String,String> headers, String encode) {
        return putData( url, stringJson, headers,  encode,SOCKET_TIME_OUT,CONNECT_TIME_OUT,READ_TIME_OUT);
    }
    public static String putData(String url,String stringJson,Map<String,String> headers, String encode,int socketTimeout,int connectTimeout,int readTimeout){
        if(encode == null){
            encode = "utf-8";
        }
        //HttpClients.createDefault()等价于 HttpClientBuilder.create().build();
        CloseableHttpClient closeableHttpClient = HttpClients.createDefault();
        HttpPut httpput = new HttpPut(url);

        //设置header
        httpput.setHeader("Content-type", "application/json");
        if (headers != null && headers.size() > 0) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpput.setHeader(entry.getKey(),entry.getValue());
            }
        }
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(readTimeout)
                .build();
        httpput.setConfig(requestConfig);
        //组织请求参数
        StringEntity stringEntity = new StringEntity(stringJson, encode);
        httpput.setEntity(stringEntity);
        String content = null;
        CloseableHttpResponse httpResponse = null;
        try {
            //响应信息
            httpResponse = closeableHttpClient.execute(httpput);
            HttpEntity entity = httpResponse.getEntity();
            content = EntityUtils.toString(entity, encode);
           return content;
        } catch (Exception e) {
            log.error("Request failed, HTTP url: {},error:{}", url, e);
            throw new BusinessException("发送put请求出现异常");
        }finally{
            closeResource(httpResponse,httpput,closeableHttpClient);

        }
    }
    /**
     * 发送http delete请求
     */
    public static String deleteData(String url, Map<String,String> headers, String encode) {
        return deleteData(url,  headers,  encode,SOCKET_TIME_OUT,CONNECT_TIME_OUT,READ_TIME_OUT);
    }
    public static String deleteData(String url, Map<String,String> headers, String encode,int socketTimeout,int connectTimeout,int readTimeout){
        if(encode == null){
            encode = "utf-8";
        }
        String content = null;
        //since 4.3 不再使用 DefaultHttpClient
        CloseableHttpClient closeableHttpClient = HttpClientBuilder.create().build();
        HttpDelete httpdelete = new HttpDelete(url);
        //设置header
        if (headers != null && headers.size() > 0) {
            for (Map.Entry<String, String> entry : headers.entrySet()) {
                httpdelete.setHeader(entry.getKey(),entry.getValue());
            }
        }
        RequestConfig requestConfig = RequestConfig.custom()
                .setSocketTimeout(socketTimeout)
                .setConnectTimeout(connectTimeout)
                .setConnectionRequestTimeout(readTimeout)
                .build();
        httpdelete.setConfig(requestConfig);
        CloseableHttpResponse httpResponse = null;
        try {
            httpResponse = closeableHttpClient.execute(httpdelete);
            HttpEntity entity = httpResponse.getEntity();
            content = EntityUtils.toString(entity, encode);
            return content;
        } catch (Exception e) {
            log.error("deleteData failed, HTTP url: {},error:{}", url, e);
            throw new BusinessException("发送delete请求出现异常");
        }finally{
            closeResource(httpResponse,httpdelete,closeableHttpClient);
        }
    }


    /**
     * 关闭连接HTTP过程产生的资源
     *
     * @param httpResponse HTTP响应
     * @param httpClient   HTTP客户端
     */
    private static void closeResource(CloseableHttpResponse httpResponse, HttpRequestBase httpMethod, CloseableHttpClient httpClient) {
        if (httpResponse != null) {
            try {
                httpResponse.close();
            } catch (IOException e) {
                log.error("Close http response error!{}", e);
            }
        }

        if (null != httpMethod) {
            httpMethod.releaseConnection();
        }

        // 关闭连接,释放资源
        if (httpClient != null) {
            try {
                httpClient.close();
            } catch (IOException e) {
                log.error("Close http client error!{}", e);
            }
        }
    }
    private static Header[] toHeaders(Map<String, String> headerMap) {
        if (null == headerMap || headerMap.isEmpty()) {
            return null;
        }

        int i = 0;
        Header[] headers = new Header[headerMap.size()];
        for (Map.Entry<String, String> entry : headerMap.entrySet()) {
            if (null != entry.getValue()) {
                headers[i++] = new BasicHeader(entry.getKey(), entry.getValue());
            }
        }

        return headers;
    }

    /**
     * 将保存POST请求的参数以及值得键值对mao转换为HTTPClient POST请求需要的List<NameValuePair>类型
     *
     * @param paramMap 请求参数
     */
    private static List<NameValuePair> toNameValuePairList(Map<String, String> paramMap) {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();

        if (paramMap != null) {
            for (Map.Entry<String, String> entry : paramMap.entrySet()) {
                Object value = entry.getValue();
                if (value != null) {
                    if (value instanceof String[]) {
                        String[] values = (String[]) value;
                        if (values.length > 0) {
                            for (String str : values) {
                                nvps.add(new BasicNameValuePair(entry.getKey(), str));
                            }
                        }
                    } else {
                        nvps.add(new BasicNameValuePair(entry.getKey(), value.toString()));
                    }
                }
            }
        }

        return nvps;
    }

}
